package com.remoter.transport.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.BindException;
import java.net.InetSocketAddress;

import com.remoter.api.extension.annotation.ExtensionName;
import com.remoter.api.extension.support.ExtensionLoader;
import com.remoter.api.provider.IProviderService;
import com.remoter.api.transport.support.AbstractServer;
import com.remoter.api.util.Final;
import com.remoter.transport.netty.codec.MessageDecoder;
import com.remoter.transport.netty.codec.MessageEncoder;
import com.remoter.transport.netty.handler.ServerMessageHandler;

@ExtensionName("netty")
public class NettyServer extends AbstractServer{
	
	protected final NioEventLoopGroup work;
	protected final NioEventLoopGroup boss;
	protected final IProviderService provider;
	
	protected InetSocketAddress bind;
	protected Channel channel;
	
	public NettyServer(){
		this.work = new NioEventLoopGroup();
		this.boss = new NioEventLoopGroup();
		this.provider = ExtensionLoader.getService(IProviderService.class,this.configuration.getOption(Final.O_SERVER_PROVIDER));
	}
	
	@Override
	public void bind(InetSocketAddress bind)throws InterruptedException,BindException{
		this.bind = bind;
		ServerBootstrap bootstrap = new ServerBootstrap();
		bootstrap.group(this.boss,this.work);
		bootstrap.channel(NioServerSocketChannel.class);
		bootstrap.option(ChannelOption.SO_BACKLOG,128);
		bootstrap.option(ChannelOption.ALLOCATOR,PooledByteBufAllocator.DEFAULT);
		bootstrap.childOption(ChannelOption.ALLOCATOR,PooledByteBufAllocator.DEFAULT);
		bootstrap.childOption(ChannelOption.SO_KEEPALIVE,true);
		bootstrap.childHandler(new ChannelInitializer<SocketChannel>(){
			@Override
			protected void initChannel(SocketChannel socketChannel) throws Exception {
				ChannelPipeline channelPipeline = socketChannel.pipeline();
				channelPipeline.addLast(new MessageDecoder(serialization));
				channelPipeline.addLast(new MessageEncoder(serialization));
				channelPipeline.addLast(new ServerMessageHandler(provider));
			}
		});
		ChannelFuture future = bootstrap.bind(this.bind);
		future.addListener(new ChannelFutureListener(){
			@Override
			public void operationComplete(ChannelFuture future) throws Exception{
				if(future.isSuccess()){
					channel = future.channel();
					logger.info("start success ["+ NettyServer.this.bind.toString() +"]!");
				}else{
					logger.info("start fail ["+ NettyServer.this.bind.toString() +"]!");
				}
			}
		});
		future.sync();
	}

	@Override
	public void disConnect(){
		this.work.shutdownGracefully();
		this.boss.shutdownGracefully();
		if(null != this.channel){
			this.channel.close();
		}
	}

	@Override
	public boolean isAvailable(){
		if(null != this.channel && this.channel.isOpen()){
			return true;
		}
		return false;
	}
	
}